﻿using EmailConnection.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace POP3Client
{
    public class POP3
    {
        private string username;
        private string password;
        private string usernameId;
        private string userServer;
        private int emailNum;
        private List<Email> emailList;

        public POP3(string username, string password)
        {
            this.username = username;
            this.password = password;
            string[] usernameParts = username.Split('@');
            if (usernameParts.Length == 1)// 说明不含@
            {
                throw new Exception("邮箱地址必须完整");
            }
            else
            {
                usernameId = usernameParts[0];
                userServer = usernameParts[1];
            }
            this.emailNum = 0;
            this.emailList = new List<Email>();
        }

        public POP3()
        {
            this.username = "";
            this.password = "";
            this.usernameId = "";
            this.userServer = "";
            this.emailNum = 0;       
            emailList = new List<Email>();
        }


        public Email praseOriContent(string s, int id)
        {
            Email email = new Email(id);

            // 自定义解码
            using (StringReader reader = new StringReader(s))
            {
                string line = reader.ReadLine();
                bool bd = false, b64 = false;
                string bds = "";
                // 逐行读取字符串内容，直到字符串末尾
                while (line != null)
                {
                    // 处理每一行字符串
                    line = line.Trim(); // 去除末尾无意义字符
                    if (line != null && line.Contains("From:")) // From
                    {
                        if (line.Contains("UTF-8"))
                        {
                            string[] segments = line.Split("=?UTF-8?B?");
                            string user = segments[1];
                            email.from = getUser(user);
                        }
                        else
                        {
                            string tmp = line.Remove(0, 5);// 把开头的字符删掉。
                            email.from = tmp;
                        }
                    }
                    if (line != null && line.Contains("To:")) // To
                    {
                        if (line.Contains("UTF-8"))
                        {
                            string[] segments = line.Split("=?UTF-8?B?");
                            string user = segments[1];
                            email.to = getUser(user);
                        }
                        else
                        {
                            string tmp = line.Remove(0, 3);// 把开头的字符删掉。
                            email.to = tmp;
                        }
                    }
                    if (line != null && line.Contains("Subject:")) // subject
                    {
                        if (line.Contains("UTF-8"))
                        {
                            string[] segments = line.Split("=?UTF-8?B?");
                            string sub = segments[1];
                            string sub2 = sub.Remove(sub.Length - 2, 2);
                            byte[] data = Convert.FromBase64String(sub2);
                            email.subject = Encoding.UTF8.GetString(data);
                        }
                        else
                        {
                            string tmp = line.Remove(0, 8);// 把开头的字符删掉。
                            email.subject = tmp;
                        }
                    }

                    if (line != null && line.Contains("Date:")) // date
                    {
                        email.date = line.Remove(0, 5);
                    }

                    // 如果遇到了base64，则正文全都要使用base64解密
                    if (line != null && line.Contains("Content-Transfer-Encoding: base64"))
                    {
                        b64 = true;
                    }

                    // 如果在boundary为false的时候遇到空行，说明是自定义邮件的邮件内容，只有html信息，没有文本信息。
                    if (bd == false && string.IsNullOrEmpty(line) == true)
                    {
                        line = reader.ReadLine();
                        StringBuilder sb = new StringBuilder();
                        while (line != null)
                        {
                            sb.Append(line);
                            line = reader.ReadLine();
                        }

                        if (b64 == true)
                        {
                            byte[] data = Convert.FromBase64String(sb.ToString());
                            email.htmlBody = Encoding.UTF8.GetString(data);
                        }
                        else
                        {
                            email.htmlBody = sb.ToString();
                        }
                    }

                    // 如果遇到了boundary，一次性两个数据块都读取完
                    if (bd == true && line.Contains(bds))
                    {
                        // 跳过接下来的三行
                        line = reader.ReadLine();
                        line = reader.ReadLine();
                        line = reader.ReadLine();

                        // 先读取文本信息

                        StringBuilder sb_text = new StringBuilder();
                        line = reader.ReadLine();
                        while (line != null && line.Contains(bds) == false)// 必须判断是否为null
                        {
                            sb_text.Append(line.Trim());
                            line = reader.ReadLine();
                        }
                        byte[] data = Convert.FromBase64String(sb_text.ToString());
                        email.textBody = Encoding.UTF8.GetString(data);

                        // 跳过接下来的三行
                        line = reader.ReadLine();
                        line = reader.ReadLine();
                        line = reader.ReadLine();

                        // 再读取html信息

                        StringBuilder sb_html = new StringBuilder();
                        line = reader.ReadLine();
                        while (line != null && line.Contains(bds) == false)// 必须判断是否为null
                        {
                            sb_html.Append(line.Trim());
                            line = reader.ReadLine();
                        }

                        data = Convert.FromBase64String(sb_html.ToString());
                        email.htmlBody = Encoding.UTF8.GetString(data);
                    }

                    // 判断boundary
                    if (line != null && line.Contains("boundary="))
                    {
                        bd = true;
                        bds = line.Split("boundary=")[1];
                        bds = bds.Substring(1, bds.Length - 2);
                    }
                    line = reader.ReadLine();
                }
            }

            return email;
        }

        public string getUser(string sender)
        {
            try
            {
                StringBuilder tmp = new StringBuilder();
                string[] names = sender.Split(" <");
                string name = names[0];
                name = name.Remove(name.Length - 2, 2); // 去掉末尾的?=，才能进行Base64解码
                // Console.WriteLine(name);
                byte[] data = Convert.FromBase64String(name);
                tmp.Append(Encoding.UTF8.GetString(data)); // 名字
                tmp.Append(" <");
                tmp.Append(names[1]); // 邮箱
                return tmp.ToString();
            }
            catch (Exception ex)
            {
                Console.WriteLine("getUser Ex: " + ex.Message);
                return "No!";
            }
        }

        public void DisplayEmail(Email email)
        {
            Console.WriteLine("Id: " + email.id);
            Console.WriteLine("From: " + email.from);
            Console.WriteLine("To: " + email.to);
            Console.WriteLine("Date: " + email.date);
            Console.WriteLine("Body(text): \n" + email.textBody);
            Console.WriteLine("-----------------------------");
            Console.WriteLine("Body(html): \n" + email.htmlBody);
            Console.WriteLine();
            Console.WriteLine();
        }


        public List<Email> GetAll() => emailList;


        public Email getOne(int id) => emailList.FirstOrDefault(email => email.id == id);
        
        public void CheckRes(string head, string s)
        {
            if (s == null || s == "")
            {
                throw new Exception(head + "POP3服务器无响应");
            }
            if(s.Contains("ERR"))
            {
                throw new Exception(head + "发生错误");
            }
        }
        public void LogOn()
        {
            using (TcpClient client = new TcpClient("pop3." + userServer, 110))
            {
                // 创建socket
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // 尝试连接服务器                                
                IPEndPoint remoteEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
                socket.Connect(remoteEndPoint);
                Console.WriteLine("连接服务器成功");

                // 开始执行协议内容
                using (NetworkStream stream = new NetworkStream(socket))
                {
                    using (StreamReader reader = new StreamReader(stream))
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        string res = reader.ReadLine();                    
                        CheckRes("连接POP3服务器： ", res);
                        Console.WriteLine(res);

                        // 用户名
                        writer.WriteLine("user " + usernameId);
                        writer.Flush(); // 强行更新       
                        res  = reader.ReadLine();
                        CheckRes("发送用户名： ", res);
                        Console.WriteLine(res);

                        // 密码
                        writer.WriteLine("pass " + password);
                        writer.Flush(); // 强行更新
                        res = reader.ReadLine();
                        CheckRes("登录（用户名或密码）：", res);
                        Console.WriteLine(res);

                        // 发送 'quit' 命令终止会话                           
                        writer.WriteLine("quit");
                        writer.Flush(); // 强行更新
                        res = reader.ReadLine();
                        CheckRes("终止POP3会话：", res);
                        Console.WriteLine(res);                       
                    }
                }
            }
        }

        public void GetMailList()
        {
            using (TcpClient client = new TcpClient("pop3." + userServer, 110))
            {
                // 创建socket
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // 尝试连接服务器                                
                IPEndPoint remoteEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
                socket.Connect(remoteEndPoint);
                Console.WriteLine("连接服务器成功");

                // 开始执行协议内容
                using (NetworkStream stream = new NetworkStream(socket))
                {
                    using (StreamReader reader = new StreamReader(stream))
                    using (StreamWriter writer = new StreamWriter(stream))
                    {

                        string res = reader.ReadLine();
                        CheckRes("连接POP3服务器： ", res);
                        Console.WriteLine(res);

                        // 用户名
                        writer.WriteLine("user " + usernameId);
                        writer.Flush(); // 强行更新       
                        res = reader.ReadLine();
                        CheckRes("发送用户名： ", res);
                        Console.WriteLine(res);

                        // 密码
                        writer.WriteLine("pass " + password);
                        writer.Flush(); // 强行更新
                        res = reader.ReadLine();
                        CheckRes("登录（用户名或密码）：", res);
                        Console.WriteLine(res);

                        // 获取邮件数量和状态
                        writer.WriteLine("stat");
                        writer.Flush(); // 强行更新
                        res = reader.ReadLine();
                        CheckRes("获取邮件数量：", res);
                        Console.WriteLine(res);     
                        
                        string[] statParts = res.Split(' ');
                        emailNum = int.Parse(statParts[1]);
                        for (int i = 1; i <= emailNum; i++)
                        {
                            // 发送 'retr' 命令检索邮件内容
                            writer.WriteLine("retr " + i);
                            writer.Flush(); // 强行更新                                
                            StringBuilder contentBuilder = new StringBuilder();                            
                            
                            string line = reader.ReadLine();
                            CheckRes("获取第" + i.ToString() + "封邮件：", line);
                            while (line != ".")
                            {
                                contentBuilder.AppendLine(line);
                                line = reader.ReadLine();
                            }
                            Email email = praseOriContent(contentBuilder.ToString(), i);
                            emailList.Add(email);
                        }

                        // 发送 'quit' 命令终止会话                           
                        writer.WriteLine("quit");
                        writer.Flush(); // 强行更新       
                        res = reader.ReadLine();
                        CheckRes("终止POP3会话：", res);
                        Console.WriteLine(res);
                    }
                }
            }

        }


        public void DisplayList()
        {
            foreach (Email email in emailList)
            {
                DisplayEmail(email);
            }
        }

        public void DeleteList(List<int> delList)
        {           
            using (TcpClient client = new TcpClient("pop3." + userServer, 110))
            {
                // 创建socket
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // 尝试连接服务器                                
                IPEndPoint remoteEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
                socket.Connect(remoteEndPoint);
                Console.WriteLine("连接服务器成功");

                // 开始执行协议内容
                using (NetworkStream stream = new NetworkStream(socket))
                {
                    using (StreamReader reader = new StreamReader(stream))
                    using (StreamWriter writer = new StreamWriter(stream))
                    {

                        string res = reader.ReadLine();
                        CheckRes("连接POP3服务器： ", res);
                        Console.WriteLine(res);

                        // 用户名
                        writer.WriteLine("user " + usernameId);
                        writer.Flush(); // 强行更新       
                        res = reader.ReadLine();
                        CheckRes("发送用户名： ", res);
                        Console.WriteLine(res);

                        // 密码
                        writer.WriteLine("pass " + password);
                        writer.Flush(); // 强行更新
                        res = reader.ReadLine();
                        CheckRes("登录（用户名或密码）：", res);
                        Console.WriteLine(res);

                        // 获取邮件数量和状态
                        writer.WriteLine("stat");
                        writer.Flush(); // 强行更新
                        res = reader.ReadLine();
                        CheckRes("获取邮件数量：", res);
                        Console.WriteLine(res);

                        string[] statParts = res.Split(' ');
                        emailNum = int.Parse(statParts[1]);

                        // 删除指定邮件
                        foreach (int id in delList)
                        {
                            if (id > emailNum) continue;
                            writer.WriteLine("dele " + id.ToString());
                            writer.Flush(); // 强行更新
                            CheckRes("删除第" + id.ToString() + "封邮件： ", res);
                            Console.WriteLine(res);
                        }

                        // 发送 'quit' 命令终止会话                           
                        writer.WriteLine("quit");
                        writer.Flush(); // 强行更新       
                        res = reader.ReadLine();
                        CheckRes("终止POP3会话：", res);
                        Console.WriteLine(res);
                    }
                }
            }          
        }
    }
}
